﻿// -------------------------------------------------------------------------------------------------------------------------------
// <copyright file="DenseVectorExtensions.cs" company="Exul">
//     Copyright (c) Exul. All rights reserved.
// </copyright>
// <author>
//     Exul
// </author>
// -------------------------------------------------------------------------------------------------------------------------------

namespace ExulLibrary.Mathematics.LinearAlgebra.Sequential.Int64Extensions
{
    #region Usings

    using System;
    using System.Globalization;

    using ExulLibrary.Exceptions;

    #endregion

    /// <summary>
    ///     Provides extension methods for <see cref="DenseVector{T}"/> with <see cref="long"/> type parameter.
    /// </summary>
    public static class DenseVectorExtensions
    {
        #region Methods

        #region public

        /// <summary>
        ///     Adds the specified factor to all elements of the vector.
        /// </summary>
        /// <param name="vector">
        ///     The vector whose elements should be added.
        /// </param>
        /// <param name="factor">
        ///     The factor to add.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="vector"/> is <see langword="null"/>.
        /// </exception>
        public static void Add(this DenseVector<long> vector, long factor)
        {
#if SAFE

            if (vector == null)
            {
                throw new ArgumentNullException("vector");
            }

#endif

            var length = vector.Length;
            var values = vector.Values;

            for (var index = 0; index < length; index++)
            {
                values[index] += factor;
            }
        }

        /// <summary>
        ///     Adds the second vector to the first vector.
        /// </summary>
        /// <param name="augend">
        ///     The augend vector.
        /// </param>
        /// <param name="addend">
        ///     The addend vector.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="augend"/> is <see langword="null"/>.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="addend"/> is <see langword="null"/>.
        ///     </para>
        /// </exception>
        /// <remarks>
        ///     The number of elements of <paramref name="addend"/> to add to <paramref name="augend"/> is determined as
        ///     min(<paramref name="augend"/>.Length, <paramref name="addend"/>.Length).
        /// </remarks>
        public static void Add(this DenseVector<long> augend, DenseVector<long> addend)
        {
#if SAFE

            if (augend == null)
            {
                throw new ArgumentNullException("augend");
            }

            if (addend == null)
            {
                throw new ArgumentNullException("addend");
            }

#endif

            var length = Math.Min(augend.Length, addend.Length);
            var augendValues = augend.Values;
            var addendValues = addend.Values;

            for (var index = 0; index < length; index++)
            {
                augendValues[index] += addendValues[index];
            }
        }

        /// <summary>
        ///     Adds the second vector to the first vector multiplied by the factor.
        /// </summary>
        /// <param name="augend">
        ///     The augend vector.
        /// </param>
        /// <param name="augendFactor">
        ///     The factor of the augend vector.
        /// </param>
        /// <param name="addend">
        ///     The addend vector.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="augend"/> is <see langword="null"/>.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="addend"/> is <see langword="null"/>.
        ///     </para>
        /// </exception>
        /// <remarks>
        ///     The number of elements of <paramref name="addend"/> to add to <paramref name="augend"/> is determined as
        ///     min(<paramref name="augend"/>.Length, <paramref name="addend"/>.Length).
        /// </remarks>
        public static void Add(this DenseVector<long> augend, long augendFactor, DenseVector<long> addend)
        {
#if SAFE

            if (augend == null)
            {
                throw new ArgumentNullException("augend");
            }

            if (addend == null)
            {
                throw new ArgumentNullException("addend");
            }

#endif

            var augendValues = augend.Values;
            var addendValues = addend.Values;

            var length = Math.Min(augend.Length, addend.Length);
            for (var index = 0; index < length; index++)
            {
                augendValues[index] = (augendFactor * augendValues[index]) + addendValues[index];
            }
        }

        /// <summary>
        ///     Adds the second vector multiplied by the factor to the first vector.
        /// </summary>
        /// <param name="augend">
        ///     The augend vector.
        /// </param>
        /// <param name="addend">
        ///     The addend vector.
        /// </param>
        /// <param name="addendFactor">
        ///     The factor of the addend vector.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="augend"/> is <see langword="null"/>.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="addend"/> is <see langword="null"/>.
        ///     </para>
        /// </exception>
        /// <remarks>
        ///     The number of elements of <paramref name="addend"/> to add to <paramref name="augend"/> is determined as
        ///     min(<paramref name="augend"/>.Length, <paramref name="addend"/>.Length).
        /// </remarks>
        public static void Add(this DenseVector<long> augend, DenseVector<long> addend, long addendFactor)
        {
#if SAFE

            if (augend == null)
            {
                throw new ArgumentNullException("augend");
            }

            if (addend == null)
            {
                throw new ArgumentNullException("addend");
            }

#endif

            var augendValues = augend.Values;
            var addendValues = addend.Values;

            var length = Math.Min(augend.Length, addend.Length);
            for (var index = 0; index < length; index++)
            {
                augendValues[index] += addendFactor * addendValues[index];
            }
        }

        /// <summary>
        ///     Adds the second vector multiplied by the second factor to the first vector multiplied by the first factor.
        /// </summary>
        /// <param name="augend">
        ///     The augend vector.
        /// </param>
        /// <param name="augendFactor">
        ///     The factor of the augend factor.
        /// </param>
        /// <param name="addend">
        ///     The addend vector.
        /// </param>
        /// <param name="addendFactor">
        ///     The factor of the addend vector.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="augend"/> is <see langword="null"/>.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="addend"/> is <see langword="null"/>.
        ///     </para>
        /// </exception>
        /// <remarks>
        ///     The number of elements of <paramref name="addend"/> to add to <paramref name="augend"/> is determined as
        ///     min(<paramref name="augend"/>.Length, <paramref name="addend"/>.Length).
        /// </remarks>
        public static void Add(
            this DenseVector<long> augend, long augendFactor, DenseVector<long> addend, long addendFactor)
        {
#if SAFE

            if (augend == null)
            {
                throw new ArgumentNullException("augend");
            }

            if (addend == null)
            {
                throw new ArgumentNullException("addend");
            }

#endif

            var augendValues = augend.Values;
            var addendValues = addend.Values;

            var length = Math.Min(augend.Length, addend.Length);
            for (var index = 0; index < length; index++)
            {
                augendValues[index] = (augendFactor * augendValues[index]) + (addendFactor * addendValues[index]);
            }
        }

        /// <summary>
        ///     Adds elements of the second vector to the specified elements of the first vector.
        /// </summary>
        /// <param name="augend">
        ///     The augend vector.
        /// </param>
        /// <param name="augendIndexes">
        ///     The indexes of augend elements.
        /// </param>
        /// <param name="addend">
        ///     The addend vector.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="augend"/> is <see langword="null"/>.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="augendIndexes"/> is <see langword="null"/>.
        ///     </para>
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="addend"/> is <see langword="null"/>.
        ///     </para>
        /// </exception>
        /// <exception cref="ElementIndexIsOutOfRangeException">
        ///     An element of <paramref name="augendIndexes"/> is negative or greater than or equal to length of
        ///     <paramref name="augend"/>.
        /// </exception>
        /// <remarks>
        ///     The number of elements of <paramref name="addend"/> to add to <paramref name="augend"/> is determined as
        ///     min(<paramref name="augendIndexes"/>.Length, <paramref name="addend"/>.Length).
        /// </remarks>
        public static void Add(this DenseVector<long> augend, int[] augendIndexes, DenseVector<long> addend)
        {
#if SAFE

            if (augend == null)
            {
                throw new ArgumentNullException("augend");
            }

            if (augendIndexes == null)
            {
                throw new ArgumentNullException("augendIndexes");
            }

            if (addend == null)
            {
                throw new ArgumentNullException("addend");
            }

#endif

            var augendValues = augend.Values;
            var addendValues = addend.Values;

            var length = Math.Min(augendIndexes.Length, addend.Length);
            for (var index = 0; index < length; index++)
            {
                var elementIndex = augendIndexes[index];

#if SAFE

                if (elementIndex < 0 || augend.Length <= elementIndex)
                {
                    var argumentName = string.Format(CultureInfo.CurrentCulture, "augendIndexes[{0}]", index);
                    throw new ElementIndexIsOutOfRangeException(argumentName, index, 0, augend.Length - 1);
                }

#endif

                augendValues[elementIndex] += addendValues[index];
            }
        }

        /// <summary>
        ///     Adds elements of the second vector to the specified elements of the first vector.
        /// </summary>
        /// <param name="augend">
        ///     The augend vector.
        /// </param>
        /// <param name="augendIndexes">
        ///     The indexes of augend elements.
        /// </param>
        /// <param name="addend">
        ///     The addend vector.
        /// </param>
        /// <param name="addendFactor">
        ///     The factor of addend vector.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="augend"/> is <see langword="null"/>.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="augendIndexes"/> is <see langword="null"/>.
        ///     </para>
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="addend"/> is <see langword="null"/>.
        ///     </para>
        /// </exception>
        /// <exception cref="ElementIndexIsOutOfRangeException">
        ///     An element of <paramref name="augendIndexes"/> is negative or greater than or equal to length of
        ///     <paramref name="augend"/>.
        /// </exception>
        /// <remarks>
        ///     The number of elements of <paramref name="addend"/> to add to <paramref name="augend"/> is determined as
        ///     min(<paramref name="augendIndexes"/>.Length, <paramref name="addend"/>.Length).
        /// </remarks>
        public static void Add(
            this DenseVector<long> augend, int[] augendIndexes, DenseVector<long> addend, long addendFactor)
        {
#if SAFE

            if (augend == null)
            {
                throw new ArgumentNullException("augend");
            }

            if (augendIndexes == null)
            {
                throw new ArgumentNullException("augendIndexes");
            }

            if (addend == null)
            {
                throw new ArgumentNullException("addend");
            }

#endif

            var augendValues = augend.Values;
            var addendValues = addend.Values;

            var length = Math.Min(augendIndexes.Length, addend.Length);
            for (var index = 0; index < length; index++)
            {
                var elementIndex = augendIndexes[index];

#if SAFE

                if (elementIndex < 0 || augend.Length <= elementIndex)
                {
                    var argumentName = string.Format(CultureInfo.CurrentCulture, "augendIndexes[{0}]", index);
                    throw new ElementIndexIsOutOfRangeException(argumentName, index, 0, augend.Length - 1);
                }

#endif

                augendValues[elementIndex] += addendFactor * addendValues[index];
            }
        }

        /// <summary>
        ///     Calculates dot product of the vector by itself.
        /// </summary>
        /// <param name="vector">
        ///     The vector to dot product.
        /// </param>
        /// <returns>
        ///     Dot product of the vector by itself.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="vector"/> is <see langword="null"/>.
        /// </exception>
        public static long DotProduct(this DenseVector<long> vector)
        {
#if SAFE

            if (vector == null)
            {
                throw new ArgumentNullException("vector");
            }

#endif

            var values = vector.Values;

            var sum = 0L;

            var length = vector.Length;
            for (var index = 0; index < length; index++)
            {
                var item = values[index];
                sum += item * item;
            }

            return sum;
        }

        /// <summary>
        ///     Calculates dot product of the first vector by the second vector.
        /// </summary>
        /// <param name="multiplicand">
        ///     The multiplicand vector.
        /// </param>
        /// <param name="multiplier">
        ///     The multiplier vector.
        /// </param>
        /// <returns>
        ///     Dot product of the first vector by the second vector.
        /// </returns>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="multiplicand"/> is <see langword="null"/>.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="multiplier"/> is <see langword="null"/>.
        ///     </para>
        /// </exception>
        /// <remarks>
        ///     The number of elements of <paramref name="multiplicand"/> and <paramref name="multiplier"/> to dot product is
        ///     determined as min(<paramref name="multiplicand"/>.Length, <paramref name="multiplier"/>.Length).
        /// </remarks>
        public static long DotProduct(this DenseVector<long> multiplicand, DenseVector<long> multiplier)
        {
#if SAFE

            if (multiplicand == null)
            {
                throw new ArgumentNullException("multiplicand");
            }

            if (multiplier == null)
            {
                throw new ArgumentNullException("multiplier");
            }

#endif

            var multiplicandValues = multiplicand.Values;
            var multiplierValues = multiplier.Values;

            var sum = 0L;

            var length = Math.Min(multiplicand.Length, multiplier.Length);
            for (var index = 0; index < length; index++)
            {
                sum += multiplicandValues[index] * multiplierValues[index];
            }

            return sum;
        }

        /// <summary>
        ///     Multiplies all elements of the vector by the specified factor.
        /// </summary>
        /// <param name="vector">
        ///     The vector whose elements should be multiplied.
        /// </param>
        /// <param name="factor">
        ///     The factor to multiply.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="vector"/> is <see langword="null"/>.
        /// </exception>
        public static void Multiply(this DenseVector<long> vector, long factor)
        {
#if SAFE

            if (vector == null)
            {
                throw new ArgumentNullException("vector");
            }

#endif

            var values = vector.Values;

            var length = vector.Length;
            for (var index = 0; index < length; index++)
            {
                values[index] *= factor;
            }
        }

        /// <summary>
        ///     Subtracts the second vector from the first vector.
        /// </summary>
        /// <param name="minuend">
        ///     The minuend vector.
        /// </param>
        /// <param name="subtrahend">
        ///     The subtrahend vector.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="minuend"/> is <see langword="null"/>.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="subtrahend"/> is <see langword="null"/>.
        ///     </para>
        /// </exception>
        /// <remarks>
        ///     The number of elements of <paramref name="subtrahend"/> to subtract from <paramref name="minuend"/>
        ///     is determined as min(<paramref name="minuend"/>.Length, <paramref name="subtrahend"/>.Length).
        /// </remarks>
        public static void Subtract(this DenseVector<long> minuend, DenseVector<long> subtrahend)
        {
#if SAFE

            if (minuend == null)
            {
                throw new ArgumentNullException("minuend");
            }

            if (subtrahend == null)
            {
                throw new ArgumentNullException("subtrahend");
            }

#endif

            var minuendValues = minuend.Values;
            var subtrahendValues = subtrahend.Values;

            var length = Math.Min(minuend.Length, subtrahend.Length);
            for (var index = 0; index < length; index++)
            {
                minuendValues[index] -= subtrahendValues[index];
            }
        }

        /// <summary>
        ///     Subtracts the second vector from the first vector multiplied by the factor.
        /// </summary>
        /// <param name="minuend">
        ///     The minuend vector.
        /// </param>
        /// <param name="minuendFactor">
        ///     The factor of the minuend vector.
        /// </param>
        /// <param name="subtrahend">
        ///     The subtrahend vector.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="minuend"/> is <see langword="null"/>.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="subtrahend"/> is <see langword="null"/>.
        ///     </para>
        /// </exception>
        /// <remarks>
        ///     The number of elements of <paramref name="subtrahend"/> to subtract from <paramref name="minuend"/>
        ///     is determined as min(<paramref name="minuend"/>.Length, <paramref name="subtrahend"/>.Length).
        /// </remarks>
        public static void Subtract(this DenseVector<long> minuend, long minuendFactor, DenseVector<long> subtrahend)
        {
#if SAFE

            if (minuend == null)
            {
                throw new ArgumentNullException("minuend");
            }

            if (subtrahend == null)
            {
                throw new ArgumentNullException("subtrahend");
            }

#endif

            var minuendValues = minuend.Values;
            var subtrahendValues = subtrahend.Values;

            var length = Math.Min(minuend.Length, subtrahend.Length);
            for (var index = 0; index < length; index++)
            {
                minuendValues[index] = (minuendFactor * minuendValues[index]) - subtrahendValues[index];
            }
        }

        /// <summary>
        ///     Subtracts the second vector multiplied by the factor from the first vector.
        /// </summary>
        /// <param name="minuend">
        ///     The minuend vector.
        /// </param>
        /// <param name="subtrahend">
        ///     The subtrahend vector.
        /// </param>
        /// <param name="subtrahendFactor">
        ///     The factor of the subtrahend vector.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="minuend"/> is <see langword="null"/>.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="subtrahend"/> is <see langword="null"/>.
        ///     </para>
        /// </exception>
        /// <remarks>
        ///     The number of elements of <paramref name="subtrahend"/> to subtract from <paramref name="minuend"/>
        ///     is determined as min(<paramref name="minuend"/>.Length, <paramref name="subtrahend"/>.Length).
        /// </remarks>
        public static void Subtract(this DenseVector<long> minuend, DenseVector<long> subtrahend, long subtrahendFactor)
        {
#if SAFE

            if (minuend == null)
            {
                throw new ArgumentNullException("minuend");
            }

            if (subtrahend == null)
            {
                throw new ArgumentNullException("subtrahend");
            }

#endif

            var minuendValues = minuend.Values;
            var subtrahendValues = subtrahend.Values;

            var length = Math.Min(minuend.Length, subtrahend.Length);
            for (var index = 0; index < length; index++)
            {
                minuendValues[index] -= subtrahendFactor * subtrahendValues[index];
            }
        }

        /// <summary>
        ///     Subtracts the second vector multiplied by the second factor from the first vector multiplied by the first factor.
        /// </summary>
        /// <param name="minuend">
        ///     The minuend vector.
        /// </param>
        /// <param name="minuendFactor">
        ///     The factor of the minuend vector.
        /// </param>
        /// <param name="subtrahend">
        ///     The subtrahend vector.
        /// </param>
        /// <param name="subtrahendFactor">
        ///     The factor of the subtrahend vector.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="minuend"/> is <see langword="null"/>.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="subtrahend"/> is <see langword="null"/>.
        ///     </para>
        /// </exception>
        /// <remarks>
        ///     The number of elements of <paramref name="subtrahend"/> to subtract from <paramref name="minuend"/>
        ///     is determined as min(<paramref name="minuend"/>.Length, <paramref name="subtrahend"/>.Length).
        /// </remarks>
        public static void Subtract(
            this DenseVector<long> minuend, long minuendFactor, DenseVector<long> subtrahend, long subtrahendFactor)
        {
#if SAFE

            if (minuend == null)
            {
                throw new ArgumentNullException("minuend");
            }

            if (subtrahend == null)
            {
                throw new ArgumentNullException("subtrahend");
            }

#endif

            var minuendValues = minuend.Values;
            var subtrahendValues = subtrahend.Values;

            var length = Math.Min(minuend.Length, subtrahend.Length);
            for (var index = 0; index < length; index++)
            {
                minuendValues[index] = (minuendFactor * minuendValues[index]) - (subtrahendFactor * subtrahendValues[index]);
            }
        }

        /// <summary>
        ///     Subtracts elements of the second vector from the specified elements of the first vector.
        /// </summary>
        /// <param name="minuend">
        ///     The minuend vector.
        /// </param>
        /// <param name="minuendIndexes">
        ///     The indexes of minuend elements.
        /// </param>
        /// <param name="subtrahend">
        ///     The subtrahend vector.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="minuend"/> is <see langword="null"/>.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="minuendIndexes"/> is <see langword="null"/>.
        ///     </para>
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="subtrahend"/> is <see langword="null"/>.
        ///     </para>
        /// </exception>
        /// <exception cref="ElementIndexIsOutOfRangeException">
        ///     An element of <paramref name="minuendIndexes"/> is negative or greater than or equal to length of
        ///     <paramref name="minuend"/>.
        /// </exception>
        /// <remarks>
        ///     The number of elements of <paramref name="subtrahend"/> to subtract from <paramref name="minuend"/>
        ///     is determined as min(<paramref name="minuendIndexes"/>.Length, <paramref name="subtrahend"/>.Length).
        /// </remarks>
        public static void Subtract(this DenseVector<long> minuend, int[] minuendIndexes, DenseVector<long> subtrahend)
        {
#if SAFE

            if (minuend == null)
            {
                throw new ArgumentNullException("minuend");
            }

            if (minuendIndexes == null)
            {
                throw new ArgumentNullException("minuendIndexes");
            }

            if (subtrahend == null)
            {
                throw new ArgumentNullException("subtrahend");
            }

#endif

            var minuendValues = minuend.Values;
            var subtrahendValues = subtrahend.Values;

            var length = Math.Min(minuendIndexes.Length, subtrahend.Length);
            for (var index = 0; index < length; index++)
            {
                var elementIndex = minuendIndexes[index];

#if SAFE

                if (elementIndex < 0 || minuend.Length <= elementIndex)
                {
                    var argumentName = string.Format(CultureInfo.CurrentCulture, "minuendIndexes[{0}]", index);
                    throw new ElementIndexIsOutOfRangeException(argumentName, index, 0, minuend.Length - 1);
                }

#endif

                minuendValues[elementIndex] -= subtrahendValues[index];
            }
        }

        /// <summary>
        ///     Subtracts elements of the second vector from the specified elements of the first vector.
        /// </summary>
        /// <param name="minuend">
        ///     The minuend vector.
        /// </param>
        /// <param name="minuendIndexes">
        ///     The indexes of minuend elements.
        /// </param>
        /// <param name="subtrahend">
        ///     The subtrahend vector.
        /// </param>
        /// <param name="subtrahendFactor">
        ///     The factor of subtrahend vector.
        /// </param>
        /// <exception cref="ArgumentNullException">
        ///     <paramref name="minuend"/> is <see langword="null"/>.
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="minuendIndexes"/> is <see langword="null"/>.
        ///     </para>
        ///     <para>
        ///         --or--
        ///     </para>
        ///     <para>
        ///         <paramref name="subtrahend"/> is <see langword="null"/>.
        ///     </para>
        /// </exception>
        /// <exception cref="ElementIndexIsOutOfRangeException">
        ///     An element of <paramref name="minuendIndexes"/> is negative or greater than or equal to length of
        ///     <paramref name="minuend"/>.
        /// </exception>
        /// <remarks>
        ///     The number of elements of <paramref name="subtrahend"/> to subtract from <paramref name="minuend"/>
        ///     is determined as min(<paramref name="minuendIndexes"/>.Length, <paramref name="subtrahend"/>.Length).
        /// </remarks>
        public static void Subtract(
            this DenseVector<long> minuend, int[] minuendIndexes, DenseVector<long> subtrahend, long subtrahendFactor)
        {
#if SAFE

            if (minuend == null)
            {
                throw new ArgumentNullException("minuend");
            }

            if (minuendIndexes == null)
            {
                throw new ArgumentNullException("minuendIndexes");
            }

            if (subtrahend == null)
            {
                throw new ArgumentNullException("subtrahend");
            }

#endif

            var minuendValues = minuend.Values;
            var subtrahendValues = subtrahend.Values;

            var length = Math.Min(minuendIndexes.Length, subtrahend.Length);
            for (var index = 0; index < length; index++)
            {
                var elementIndex = minuendIndexes[index];

#if SAFE

                if (elementIndex < 0 || minuend.Length <= elementIndex)
                {
                    var argumentName = string.Format(CultureInfo.CurrentCulture, "minuendIndexes[{0}]", index);
                    throw new ElementIndexIsOutOfRangeException(argumentName, index, 0, minuend.Length - 1);
                }

#endif

                minuendValues[elementIndex] -= subtrahendFactor * subtrahendValues[index];
            }
        }

        #endregion

        #endregion
    }
}